<?php

/*

Desktopd Colors
Copyright (C) 2016  Desktopd Developer(s)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

https://www.gnu.org/licenses/agpl.html

*/


namespace Desktopd\Colors;


class CIELUV {
    protected $L;
    protected $u;
    protected $v;
    
    protected static function uFromXYZ (XYZ $XYZ) {
        $X = $XYZ->getX();
        $Y = $XYZ->getY();
        $Z = $XYZ->getZ();
        return 4 * $X / ($X + 15 * $Y + 3 * $Z);
    }
    
    protected static function vFromXYZ (XYZ $XYZ) {
        $X = $XYZ->getX();
        $Y = $XYZ->getY();
        $Z = $XYZ->getZ();
        return 9 * $Y / ($X + 15 * $Y + 3 * $Z);
    }
    
    public static function fromXYZ (XYZ $XYZ, XYZ $whitepoint) {
        $y = $XYZ->getY() / $whitepoint->getY();
        if ($y > 0.008856451679035631) {
            $L = 116 * pow($y, 1 / 3) - 16;
        } else {
            $L = 903.2962962962961 * $y;
        }
        
        $u = 13 * $L * (self::uFromXYZ($XYZ) - self::uFromXYZ($whitepoint));
        $v = 13 * $L * (self::vFromXYZ($XYZ) - self::vFromXYZ($whitepoint));
        
        return new self($L, $u, $v);
    }
    
    public function __construct ($L, $u, $v) {
        list(
            $this->L,
            $this->u,
            $this->v
        ) = array_map(function($c) {
            if (!is_numeric($c)) {
                throw new \InvalidArgumentException(print_r($c, true) . ' is not numeric');
            }
            
            return (float) $c;
        }, array($L, $u, $v));
        
    }
    
    public function toXYZ (XYZ $whitepoint) {
        $u = $this->u / (13 * $this->L) + self::uFromXYZ($whitepoint);
        $v = $this->v / (13 * $this->L) + self::vFromXYZ($whitepoint);
        
        if ($this->L > 8) {
            $Y = $whitepoint->getY() * pow(($this->L + 16) / 116, 3);
        } else {
            $Y = $whitepoint->getY() * $this->L * 0.0011070564598794539;
        }
        
        $X = $Y * 2.25 * $u / $v;
        $Z = $Y * (12 - 3 * $u - 20 * $v) / (4 * $v);
        
        return new XYZ($X, $Y, $Z);
    }
    
    public function __toString () {
        return sprintf ('(L, u*, v*) = (%.3f, %.3f, %.3f)'
            , $this->L, $this->u, $this->v);
    }
    
    /*
        Get L
    */
    public function getL () {
        return $this->L;
    }
    
    /*
        Get u*
    */
    public function get_u () {
        return $this->u;
    }
    
    public function getU () {
        return $this->u;
    }
    
    /*
        Get v*
    */
    public function get_v () {
        return $this->v;
    }
    
    public function getV () {
        return $this->v;
    }
}


// vim: ts=4 et ai

